// NEC codes: http://www.sbprojects.com/knowledge/ir/nec.php  
// http://www.arduino.cc/playground/Main/TimerPWMCheatsheet
// http://www.arduino.cc/playground/Code/Timer1  (useful library)
// http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf (ATMega328 datasheet)
// http://asynclabs.com/wiki/index.php?title=WiServer
// http://www.sics.se/~adam/old-uip/uip-1.0-refman/
// http://mars.netanya.ac.il/~unesco/cdrom/booklet/HTML/NETWORKING/node300.html

#include "IRremote.h"
#include <Servo.h> 
#include <Ethernet.h>
#include <SPI.h>
#include <SD.h>

// IR Emitter must be on pin 3 (hardcoded in library)
// Servos must be on a PWM pin (3, 5, 6, 9, 10, 11)
// Pin 13 has a hardwired LED so is not suitable to drive other LEDs
//  (even though blink can be turned off)

// Servo class uses Timer1 and associated interrupts (11-14)
// IRRemote class uses Timer2 and associated interrupts (8-10)
//    (timer on output for PWM and on input to generate events, hence they can't be enabled at the same time)
// WiFi uses Timer0 and associated overflow interrupt, along with PCINT0

#define PIN_SERIAL_RX         0
#define PIN_SERIAL_TX         1
#define PIN_IR_TX             3
#define PIN_SPI_CS_SD         4
#define PIN_SERVO_TILT        5
#define PIN_SERVO_PAN         6
#define PIN_LED               7
#define PIN_SPI_MOSI         11
#define PIN_SPI_MISO         12
#define PIN_SPI_CLK          13

#define MAX_SERIAL_BUF_LENGTH 40
byte serial_buf[MAX_SERIAL_BUF_LENGTH];
int serial_buf_length;
int telnet_command_state;

IRsend irsend;

IPAddress ipaddr = IPAddress(192, 168, 1, 77);
IPAddress gateway = IPAddress(192, 168, 1, 1);
IPAddress dnsaddr = IPAddress(192, 168, 1, 1);
IPAddress netmask = IPAddress(255, 255, 255, 0);
byte mac[6] = {0xDE, 0xED, 0xBE, 0xEF, 0xFE, 0xED};

EthernetServer server(23);  // telnet port
EthernetClient client;

Servo servo_pan;
Servo servo_tilt;
float servo_scale;

#define BLINK_TIME 5000  
unsigned long blink_timer;

void setup() {

  pinMode(PIN_LED, OUTPUT);

  servo_scale = float(MAX_PULSE_WIDTH - MIN_PULSE_WIDTH) / 180.0;  //microseconds per degree

  servo_pan.attach(PIN_SERVO_PAN);
  servo_tilt.attach(PIN_SERVO_TILT);

  serial_buf_length = 0;
  serial_buf[0] = '\0';
  telnet_command_state = 0;

  load_config();

  Ethernet.begin(mac, ipaddr, gateway, dns, netmask);
  server.begin();
}

void loop() {

  byte b;
  
  client = server.available();
  if (! client) {
    return;
  }

  while (client.available() > 0) {
    b = client.read();
    process_serial_input(b);
  }

}

void process_serial_input(byte b) {

  if ((b == 0xFF) && (telnet_command_state == 0)) {  
    telnet_command_state = 2;
    return;
  } 

  if ((b == 0xFF) && (telnet_command_state == 2)) {  // 0xFF treated as data
    telnet_command_state = 0;
  }

  if (telnet_command_state > 0) {
    telnet_command_state--;
    return;
  }

  if (b == '\r') {
    return;
  } 

  if (b == '\n') {
    if (serial_buf[0] != '\0') {
      execute_command(serial_buf);
    }
    serial_buf[0] = '\0';
    serial_buf_length = 0;
    client.print("\r\n> ");
    return;
  } 

  if (serial_buf_length < MAX_SERIAL_BUF_LENGTH - 1) {
    serial_buf[serial_buf_length++] = b;
    serial_buf[serial_buf_length] = '\0';
  }
} 

void execute_command(byte *bufp) {

  client.print(F("Processing command: "));
  client.print((char*)bufp);
  client.print("\n");

  int command = *bufp++;

  switch (command) {
  case 'n': 
    break;
  case 'p':
    move_servo(servo_pan, bufp);
    break;
  case 't':
    move_servo(servo_tilt, bufp);
    break;
  case 'e':
    emit_ir_code(bufp);
    break;
  case 'h': 
    print_help();
    break;
  case 's':
    print_status(); 
    break;
  default:
    client.println(F("Invalid command"));
  }
}

void move_servo(Servo s, byte *bufp) {

  if ((*bufp == '=') || (*bufp == ' ')) {  // separator optional
    bufp++;
  }

  int pos = 0;
  while ((*bufp >= '0') && (*bufp <= '9')) {
    pos = pos * 10 + *bufp - '0';
    bufp++;
  }

  char scale = *bufp++;

  if ((scale != 'd') && (scale != 'u')) {
    client.print(F("Invalid scale\n"));
    return;
  }

  if (*bufp != '\0') {
    client.print(F("Extra characters:"));
    client.print((char*)bufp);
    client.print("\n");
    return;
  }  

  if (scale == 'd') {
    pos = pos * servo_scale + MIN_PULSE_WIDTH;
  }

  client.print(F("Moving servo to: "));
  client.print(pos);
  client.print(F("\n"));

  s.writeMicroseconds(pos);

} 

void emit_ir_code(byte *bufp) {

  unsigned long value = 0;
  byte command[8] = {
    0,0,0,0,0,0,0,0    };
  int bits = 0;

  if ((*bufp == '=') || (*bufp == ' ')) {  // separator optional
    bufp++;
  }

  char codetype = *bufp++;

  if ((*bufp == ':') || (*bufp == ' ')) {  // separator required
    bufp++;
  } 
  else {
    client.print(F("Invalid syntax\n"));  
    return;
  }

  int i = 0;

  while (*bufp != '\0') {  
    byte b;
    b = *bufp++;
    int n = i / 2;
    if (n > 7) {
      client.print(F("Hex string too long\n"));  
      return;
    }
    if ((b >= '0') && (b <= '9')) {
      value = (value << 4) + (b - '0');  
      command[n] = (command[n] << 4) + (b - '0');  
    } 
    else {
      if ((b >= 'a') && (b <= 'f')) {
        value = (value << 4) + (b - 'a');  
        command[n] = (command[n] << 4) + (b - 'a' + 10);  
      } 
      else {
        if ((b >= 'A') && (b <= 'F')) {
          value = (value << 4) + (b - 'A');  
          command[n] = (command[n] << 4) + (b - 'A' + 10);  
        } 
        else {
          client.print(F("Invalid hex character\n"));  
          return;
        }
      }
    }
    bits += 4;
    i++;
  }

  switch (codetype - '0') {
  case NEC:
    irsend.sendNEC(value, bits);
    client.print(F("NEC"));
    break;    
  case SONY:
    irsend.sendSony(value, bits);
    client.print(F("Sony"));
    break;    
  case RC5:
    irsend.sendRC5(value, bits);
    client.print(F("RC5"));
    break;    
  case RC6:
    irsend.sendRC6(value, bits);
    client.print(F("RC6"));
    break;    
  case PANASONIC:
    irsend.sendPanasonic(command);
    client.print(F("Panasonic"));
    break;    
  default:  
    client.print(F("Invalid code type\n"));
    return;
  }

  client.print(F(": "));
  for (int i=0; i < (bits/8); i++) {
    if (command[i] <= 16) {
      client.print('0');
    }
    client.print(command[i], HEX);
    client.print(' ');
  } 
  client.print('(');
  client.print(bits);
  client.print(F(" bits)\n"));
}


void print_status() {

  client.print(F("Pan: "));
  client.print(u2d(servo_pan.readMicroseconds()));
  client.print(F("\n"));

  client.print(F("Tilt: "));
  client.print(u2d(servo_tilt.readMicroseconds()));
  client.print(F("\n"));
}


void show_status_lights() {
  int led = LOW;

  if (blink_timer > 0) {
    blink_timer -= 1;
    led = HIGH;
  }

  digitalWrite(PIN_LED, led);
} 


void print_help() {
  client.print(F("Commands:\n"));
  client.print(F("h               Display this help information\n"));
  client.print(F("s               Display current status\n"));
  client.print(F("e t nnnnnn      Emit type t IR code nnnnnn\n"));
  client.print(F("p n[u|d]        Pan n degrees or units\n"));
  client.print(F("t n[u|d]        Tilt n degrees or units\n"));
  client.print(F("\n"));
  client.print(F("Examples:\n"));
  client.print(F("p 94d           Pan to 94 degrees\n"));
  client.print(F("e 5 ab00006ef6  emit Panasonic (type 5) code ab00006ef6\n"));
}









































